﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using Unis.Framework.Core.Exceptions;
using Unis.Framework.Core.Infrastructure;
using Unis.Framework.Core.Utility;
using Unis.Framework.ORM.Model;

namespace Unis.Framework.RuleBase.Serializer
{
    public class QueryParameterXmlSerializer : ContextBoundObject
    {
        private static readonly string GROUP_CHILDS_NAME = "Childs";
        private static readonly string GROUP_NAME = "GROUP";
        private static readonly string[] PARAM_EXCEPTION_PROS_NAME = new string[] { "IsGroupNode", "GroupNodeLinkType", "Childs" };
        private static readonly string PARAMETER_GROUP_NODE = "GROUP GroupNodeLinkType='{0}'";
        private static readonly string PARAMETER_NODE = "Parameter";
        private static readonly string QUERY_ROOT_PATH = "/FilterRoot";
        private static readonly string XML_CDATA = "<![CDATA[{0}]]>";

        public QueryParameterInfo[] DeSerializer(string queryParsXmlString)
        {
            if (string.IsNullOrEmpty(queryParsXmlString))
            {
                return null;
            }
            var lstData = new List<QueryParameterInfo>();
            var document = new XmlDocument();
            try
            {
                document.LoadXml(queryParsXmlString);
                XmlNode node = document.SelectSingleNode(QUERY_ROOT_PATH);
                if ((node != null) && (node.ChildNodes.Count > 0))
                {
                    this.FillListEntity(lstData, node.ChildNodes);
                }
            }
            catch (Exception exception)
            {
                throw new APPException("反系列化传入的查询参数有误！" + exception.Message);
            }
            return lstData.ToArray();
        }

        private void FillListEntity(List<QueryParameterInfo> lstData, XmlNodeList nodeList)
        {
            foreach (XmlNode node in nodeList)
            {
                QueryParameterInfo entity = new QueryParameterInfo();
                if (string.Compare(node.Name, GROUP_NAME, true) == 0)
                {
                    if (node.ChildNodes.Count == 0)
                    {
                        continue;
                    }
                    entity.IsGroupNode = true;
                    if ((node.Attributes["GroupNodeLinkType"] != null) && !string.IsNullOrEmpty(node.Attributes["GroupNodeLinkType"].Value))
                    {
                        entity.GroupNodeLinkType = (QueryGroupLinkType)Enum.Parse(typeof(QueryGroupLinkType), node.Attributes["GroupNodeLinkType"].Value);
                    }
                    else
                    {
                        entity.GroupNodeLinkType = QueryGroupLinkType.AND;
                    }
                    List<QueryParameterInfo> list = new List<QueryParameterInfo>();
                    entity.Childs = list;
                    this.FillListEntity(list, node.ChildNodes);
                }
                else
                {
                    this.FillSingleProperty(entity, node);
                }
                lstData.Add(entity);
            }
        }

        private void FillSingleProperty(QueryParameterInfo entity, XmlNode xmlNode)
        {
            foreach (PropertyInfo info in entity.GetType().GetProperties())
            {
                if ((!info.IsSpecialName && info.CanWrite) && ((Attribute.GetCustomAttribute(info, typeof(DataMemberAttribute)) is DataMemberAttribute) && (Array.IndexOf<string>(PARAM_EXCEPTION_PROS_NAME, info.Name) < 0)))
                {
                    XmlNode node = GetNodeByNodeName(xmlNode.ChildNodes, info.Name);
                    if ((node != null) && !string.IsNullOrEmpty(node.InnerText))
                    {
                        object val = node.InnerText.Trim();
                        if (info.PropertyType.IsEnum)
                        {
                            val = Enum.Parse(info.PropertyType, val.ToString());
                        }
                        Reflection.SetObjectProperty(entity, info.Name, val);
                    }
                }
            }
        }

        private static XmlNode GetNodeByNodeName(XmlNodeList nodeList, string nodeName)
        {
            foreach (XmlNode node in nodeList)
            {
                if (string.Compare(node.Name, nodeName, true) == 0)
                {
                    return node;
                }
            }
            return null;
        }

        private void SerializeListData(StringBuilder sXml, List<QueryParameterInfo> lstData)
        {
            foreach (QueryParameterInfo info in lstData)
            {
                if (info.IsGroupNode)
                {
                    if ((info.Childs != null) && (info.Childs.Count > 0))
                    {
                        WriteFirstMarker(sXml, string.Format(PARAMETER_GROUP_NODE, info.GroupNodeLinkType.ToString()));
                        this.SerializeListData(sXml, info.Childs);
                        WriteLastMarker(sXml, "GROUP");
                    }
                }
                else
                {
                    WriteFirstMarker(sXml, PARAMETER_NODE);
                    this.SerializeSingleObject(sXml, info);
                    WriteLastMarker(sXml, PARAMETER_NODE);
                }
            }
        }

        public string Serializer(QueryParameterInfo[] queryPars)
        {
            StringBuilder sXml = new StringBuilder();
            sXml.Append(string.Format("<FilterRoot AdvanceFilter='{0}'>", "False"));
            if ((queryPars != null) && (queryPars.Length > 0))
            {
                this.SerializeListData(sXml, new List<QueryParameterInfo>(queryPars));
            }
            sXml.Append("</FilterRoot>");
            return sXml.ToString();
        }

        private void SerializeSingleObject(StringBuilder sXml, QueryParameterInfo val)
        {
            foreach (PropertyInfo info in val.GetType().GetProperties())
            {
                if (!info.IsSpecialName)
                {
                    object[] customAttributes = info.GetCustomAttributes(typeof(DataMemberAttribute), true);
                    if (((customAttributes != null) && (customAttributes.Length != 0)) && (Array.IndexOf<string>(PARAM_EXCEPTION_PROS_NAME, info.Name) < 0))
                    {
                        Type propertyType = info.PropertyType;
                        object obj2 = info.GetValue(val, null);
                        if (obj2 != null)
                        {
                            WriteFirstMarker(sXml, info.Name);
                            if ((info.Name == "Value") || (info.Name == "Value2"))
                            {
                                ValueToXmlString(sXml, obj2, propertyType);
                            }
                            else
                            {
                                sXml.Append(obj2.ToString());
                            }
                            WriteLastMarker(sXml, info.Name);
                        }
                    }
                }
            }
        }

        private static void ValueToXmlString(StringBuilder sXml, object val, Type valType)
        {
            if ((val != null) && !string.IsNullOrEmpty(val.ToString()))
            {
                if (valType.Equals(typeof(DateTime)))
                {
                    DateTime time = (DateTime)val;
                    sXml.Append(string.Format(XML_CDATA, time.ToString("yyyy-MM-dd HH:mm:ss")));
                }
                else if (valType.IsGenericType && valType.Equals(typeof(DateTime?)))
                {
                    DateTime time2 = (DateTime)val;
                    sXml.Append(string.Format(XML_CDATA, time2.ToString("yyyy-MM-dd HH:mm:ss")));
                }
                else
                {
                    sXml.Append(string.Format(XML_CDATA, val));
                }
            }
        }

        private static void WriteFirstMarker(StringBuilder sXml, string nodeName)
        {
            sXml.Append("<");
            sXml.Append(nodeName);
            sXml.Append(">");
        }

        private static void WriteLastMarker(StringBuilder sXml, string nodeName)
        {
            sXml.Append("</");
            sXml.Append(nodeName);
            sXml.Append(">");
        }

        //public static QueryParameterXmlSerializer _instance = null;

        // Properties
        public static QueryParameterXmlSerializer DefaultInstance
        {
            get
            {
                //if (QueryParameterXmlSerializer._instance == null)
                //{
                //    QueryParameterXmlSerializer._instance = new QueryParameterXmlSerializer();
                //}
                //return QueryParameterXmlSerializer._instance;
                return Singleton<QueryParameterXmlSerializer>.InstanceObj;
            }
        }
    }
}
